Skip to content

feat(dashboard): replace the fixed Cycle Time sentence with a computed, honest verdict#110

Open
marcos-sabatino-clickbus wants to merge 3 commits into
mainfrom
feat/cycle-time-honest-verdict
Open

feat(dashboard): replace the fixed Cycle Time sentence with a computed, honest verdict#110
marcos-sabatino-clickbus wants to merge 3 commits into
mainfrom
feat/cycle-time-honest-verdict

Conversation

@marcos-sabatino-clickbus

Copy link
Copy Markdown

What & why

The Cycle Time card showed a hardcoded editorial sentence — "lead-time bottlenecks live before (demand/product) and after (infra, environments, deploy) — not in code execution" — rendered identically for every tenant, not derived from any data. This replaces it with a verdict computed from the tenant's own flow data, and surfaces the per-phase decomposition the engine already produced but the org dashboard discarded.

The verdict only ever describes the window it measures (PR open → merge). It makes no claim about the "before" (demand) or "after" (deploy) windows the engine doesn't measure here.

How

  • Aggregate the per-PR phase medians (time_in_phase_median_hours) the engine already emits into an org-level decomposition (summarizeFlow), weighted by the real count of decomposed PRs.
  • Compute the dominant phase + its share over the post-open window only (WINDOW_PHASES) — coding (pre-PR authoring) never wins.
  • Gate the verdict on density (≥50 merged PRs) and coverage (≥60%). Below that it shows a partial-sample note or stays silent — never a confident claim.
  • Render a phase bar (reusing the engine's decomposition), hatching wait-phase segments so active-vs-wait reads at a glance.
  • Engine now emits flow_pr_count — the honest coverage numerator (pr_count already existed on FlowEfficiencyResult, it just wasn't persisted).

Commits

  1. feat — compute the verdict + phase bar from data already in the payload.
  2. fix — four accuracy majors from an adversarial review: share-denominator wording, exclude pre-PR coding, make the coverage guard-rail actually live (flow_pr_count), drop a redundant wait tag.
  3. fix — gate the phase bar to the affirmative verdict variant (a UI consistency regression the coverage fix exposed) and re-express wait/active visually.

Verification

  • TS suite: 231 passing · tsc --noEmit: clean · ESLint: clean.
  • New logic (lib/queries/cycle-time-flow.ts): 100% lines / 97.5% branches / 100% functions.
  • Python suite: 239 passing (engine flow_pr_count).
  • No DB migration, no new ingestion source, no engine analysis change beyond emitting an existing count.

Deploy note

Real coverage requires repos re-analyzed with the new engine so flow_pr_count reaches the payload. Until then, older payloads → flow = null → the card shows a neutral state with no causal claim. Graceful by design.

Non-blocking follow-ups

  • Align the low-coverage banner copy with the now-hidden phase bar.
  • Hatch contrast over the yellow phase color; replicate the hatch in the legend dots (a11y).
  • The presentational layer (buildVerdictText, FlowPhaseBar) isn't unit-tested — vitest runs in node (no jsdom) in this repo.

Implements opportunity E1 from the platform evaluation; hardened through a review → fix → re-review loop.

🤖 Generated with Claude Code

marcos-sabatino-clickbus and others added 3 commits June 20, 2026 01:52
Replace the hardcoded "lead-time bottlenecks live before/after, not in
code execution" sentence in the Cycle Time card with a verdict computed
from the tenant's own flow data.

The engine already emits per-PR phase medians (time_in_phase_median_hours)
and the per-repo page renders them, but the org dashboard still showed a
fixed editorial string. This aggregates those phases org-wide (weighted by
merged PRs), picks the dominant phase, and renders a stacked phase bar. The
verdict only ever describes the code window (PR open -> merge); it never
claims anything about the "before" (demand) or "after" (deploy) windows the
engine does not measure here.

- summarizeFlow / selectCycleTimeVerdict: pure, fully unit-tested
  (100% lines, ~98% branches on the new module)
- coverage-gated: below 60% the card shows a partial sample, not a verdict
- aggregated by repo/org only, never per person
- no DB migration, no new ingestion, no engine change

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rdict

An adversarial self-review of the first commit found four accuracy
defects in the "honest verdict" card. This fixes all four.

M1 - share denominator: relabel "% of cycle time" -> "% of measured
phase time" and document that the window total is a SUM of phase medians,
not the cycle-time median (a separate open->merge aggregation shown
alongside).

M2 - coding contamination: the dominant phase and its share are now
computed over WINDOW_PHASES (the 4 post-open phases). `coding`
(first commit -> PR open) is pre-window authoring time and can no longer
be reported as the bottleneck of a card that measures only PR open->merge.
The phase bar shows the window phases only.

M3 - coverage guard-rail was inert: flowCoveragePct used pr_merged_count
as both numerator and denominator (always ~100%). The engine now emits
flow_pr_count (the real decomposed-PR count; pr_count already existed on
FlowEfficiencyResult), and the platform weights/measures coverage by it.
Payloads without flow_pr_count are skipped so coverage never fakes 100%.

M4 - removed the redundant "(wait)/(espera)" tag, which only ever
appended to phase labels that already said "wait/espera".

Tests rewritten for the new semantics (weight, window dominant, real
coverage) with cases locking M2 (coding never wins) and M3 (coverage < 1
and null without flow_pr_count). TS 231 pass, Python 239 pass, tsc clean,
cycle-time-flow.ts 100% lines / 97.5% branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ctive

Follow-up from the post-fix re-review, which found one new major (a UI
consistency regression that the M3 coverage fix made routine) plus the
orphaned isWait field.

- FlowPhaseBar now renders only when verdict.variant === "verdict".
  Before, it showed in lowCoverage and none too (dominantPhase is set in
  every variant), so an affirmative phase bar appeared next to a "partial
  sample, not a verdict" banner — and below the density floor with no
  banner at all. Honest coverage (M3) made lowCoverage common, so this
  was about to ship as a systematic contradiction.

- Re-expose the wait/active signal lost when M4 dropped the waitTag: the
  phase bar now hatches wait-phase segments (via WAIT_PHASES) with a short
  legend note, instead of a redundant text tag. The orphaned
  DominantPhase.isWait field (its only reader was the removed waitTag) is
  deleted; tests assert wait-ness via WAIT_PHASES directly.

TS 231 pass, tsc clean, cycle-time-flow.ts 100% lines / 97.5% branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clickbus-iris Ready Ready Preview, Comment Jun 20, 2026 6:33am

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant